在 2020 年上半年,Webpack 提出了一项非常激动人心的特性——Module Federation(译为模块联邦),这个特性一经推出就获得了业界的广泛关注,甚至被称为前端构建领域的Game Changer。实际上,这项技术确实很好地解决了多应用模块复用的问题,相比之前的各种解决方案,它的解决方式更加优雅和灵活。但从另一个角度来说,Module Federation 代表的是一种通用的解决思路,并不局限于某一个特定的构建工具,因此,在 Vite 中我们同样可以实现这个特性,并且社区已经有了比较成熟的解决方案。
在接下来的文章中,首先我将和你一起深入探讨 Module Federation(简称 MF) 的核心概念,分析它到底解决了什么问题、对于这些问题原来存在哪些解决方案、为什么 MF的方案更优。然后我会用一个具体的项目示例带你进行代码实操,让你学会在 Vite 正确地使用 MF 特性。当然,在最后我也会给大家剖析 Module Federation 内部的实现原理,让你不仅仅停留在会用的地步,而且也能了解其深层的运作机制和实现手段。
# 模块共享之痛
对于一个互联网产品来说,一般会有不同的细分应用,比如腾讯文档可以分为word、excel、ppt等等品类,抖音 PC 站点可以分为短视频站点、直播站点、搜索站点等子站点,而每个子站又彼此独立,可能由不同的开发团队进行单独的开发和维护,看似没有什么问题,但实际上会经常遇到一些模块共享的问题,也就是说不同应用中总会有一些共享的代码,比如公共组件、公共工具函数、公共第三方依赖等等。对于这些共享的代码,除了通过简单的复制粘贴,还有没有更好的复用手段?
# 1. 发布 npm 包
发布 npm 包是一种常见的复用模块的做法,我们可以将一些公用的代码封装为一个 npm 包,具体的发布更新流程是这样的:
- 公共库 lib1 改动,发布到 npm;
- 所有的应用安装新的依赖,并进行联调。

封装 npm 包可以解决模块复用的问题,但它本身又引入了新的问题:
- 开发效率问题。每次改动都需要发版,并所有相关的应用安装新依赖,流程比较复杂。
- 项目构建问题。引入了公共库之后,公共库的代码都需要打包到项目最后的产物后,导致产物体积偏大,构建速度相对较慢。
因此,这种方案并不能作为最终方案,只是暂时用来解决问题的无奈之举。
# 2. Git Submodule
通过 git submodule 的方式,我们可以将代码封装成一个公共的 Git 仓库,然后复用到不同的应用中,但也需要经历如下的步骤:
- 公共库 lib1 改动,提交到 Git 远程仓库;
- 所有的应用通过
git submodule命令更新子仓库代码,并进行联调。
你可以看到,整体的流程其实跟发 npm 包相差无几,仍然存在 npm 包方案所存在的各种问题。
# 3. 依赖外部化(external)+ CDN 引入
在上一节中我们提到了external的概念,即对于某些第三方依赖我们并不需要让其参与构建,而是使用某一份公用的代码。按照这个思路,我们可以在构建引擎中对某些依赖声明external,然后在 HTML 中加入依赖的 CDN 地址:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<!-- 从 CDN 上引入第三方依赖的代码 -->
<script src="https://cdn.jsdelivr.net/npm/react@17.0.2/index.min.js">